﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ITAP.ShprossesBuilder.Modelling.Components.Constraints;
using System.Drawing;
using ITAP.Core.Modelling.Primitives;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components
{
    [Serializable]
    public class SpacerContainer : ISpacer
    {
        public double SpacerWidth
        {
            get
            {
                foreach (IComponent innerComponent in this.innerComponents)
                    if (innerComponent is Spacer)
                        return (innerComponent as Spacer).SpacerWidth;

                throw new Exception("В контейнере рамок нет ни одной рамки.");
            }
        }

        public bool IsInSpacer(ModelPoint point)
        {
            
            foreach (IComponent innerComponent in this.innerComponents)
                if (innerComponent is Spacer && (innerComponent as Spacer).IsInSpacer(point))
                    return true;

            return false;
        }
            

        public SpacerContainer()
        {
            this.connectedComponents = new List<IComponent>();
            this.innerComponents = new List<IComponent>();
            this.constraints = new List<IConstraint>();
            this.criticalContraintTypes = new List<Type>();
            this.primitives = new List<IPrimitive>();
            this.acceptedComponentTypesToConnect = new ConnectionInfo();
            this.componentsInTolerance = new List<IComponent>();
        }

        private int serialNumber;
        #region IComponent Members

        public int SerialNumber
        {
            get
            {
                return serialNumber;
            }
            set
            {
                serialNumber = value;
            }
        }

        private List<IConstraint> constraints;
        public List<ITAP.ShprossesBuilder.Modelling.Components.Constraints.IConstraint> Constraints
        {
            get { return constraints; }
        }

        private List<IComponent> connectedComponents;
        public List<IComponent> ConnectedComponents
        {
            get { return connectedComponents; }
        }

        private List<IComponent> innerComponents;
        public List<IComponent> InnerComponents
        {
            get { return innerComponents; }
        }

        public List<IComponent> AllInnerComponents
        {
            get 
            {
                return innerComponents;
            }
        }

        public double Tolerance
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }


        public System.Drawing.RectangleF BoundingBox
        {
            get 
            {
                RectangleF boundingBox = innerComponents[0].BoundingBox;
                for (int i = 0; i < innerComponents.Count; i++)
                    boundingBox = RectangleF.Union(innerComponents[i].BoundingBox, boundingBox);
                return boundingBox;
            }
        }

        private bool isUpdateRequired = true;
        public bool IsUpdateRequired
        {
            get
            {
                return isUpdateRequired;
            }
            set
            {
                isUpdateRequired = value;
            }
        }

        private List<IPrimitive> primitives;
        public List<ITAP.Core.Modelling.Primitives.IPrimitive> Primitives
        {
            get { return primitives; }
        }

        public IComponent ConnectTo(IComponent component)
        {
            throw new NotImplementedException();
        }

        public IComponent DisconnectFrom(IComponent component)
        {
            throw new NotImplementedException();
        }

        public bool CanConnect(IComponent component)
        {
            throw new NotImplementedException();
        }

        public void Update()
        {
            foreach (IConstraint constraint in this.constraints)
            {
                bool isChecked = false;
                try
                {
                    isChecked = constraint.Check(this);
                }
                catch (Exception ex)
                {
                    FatalErrorEventArgs e = new FatalErrorEventArgs();
                    e.FatalConstraint = constraint;
                    e.FatalException = ex;
                    this.FatalModelError.Invoke(this, e);
                    continue;
                }

                if (!isChecked)
                {
                    ComponentConstraintEventArgs e = new ComponentConstraintEventArgs(constraint);
                    this.BeforeFixingConstraint.Invoke(this, e);
                    if (!e.CancelFixing)
                        constraint.Fix(this);
                    this.AfterFixingConstraint.Invoke(this, e);

                }
            }
        }

        public void AcceptVisitor(IModelVisitor visitor)
        {
          //  visitor.Visit(this);
        }

        public bool IsInTolerance(ModelPoint point)
        {
            return false;
        }

        public bool IsInTolerance(ModelPoint point, double tolerance)
        {
            return false;
        }

        public List<IComponent> GetComponentsByPoint(ModelPoint point, double tolerance)
        {
            throw new NotImplementedException();
        }

        public bool MoveTo(ModelPoint destination)
        {
            throw new NotImplementedException();
        }

        public bool MoveTo(ModelPoint destination, System.Drawing.RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public bool MoveWithOffset(ModelVector offset)
        {
            foreach (IComponent innerComponent in this.innerComponents)
                innerComponent.MoveWithOffset(offset);
            return true;
        }

        public bool MoveWithOffset(ModelVector offset, System.Drawing.RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public bool Rotate(double angleOffset, ModelPoint rotateCenter)
        {
            throw new NotImplementedException();
        }

        public bool Rotate(double angleOffset, ModelPoint rotateCenter, System.Drawing.RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public void Rotate(double angleOffset)
        {
            throw new NotImplementedException();
        }

        public bool Rotate(double angleOffset, System.Drawing.RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public ModelPoint Position
        {
            get { throw new NotImplementedException(); }
        }

        public ITAP.Core.Modelling.Primitives.EndType GetComponentNearestEndType(IComponent component)
        {
            throw new NotImplementedException();
        }

        public List<Type> PreUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        public List<Type> PostUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        private IComponent parent;
        public IComponent Parent
        {
            get
            {
                return parent;
            }
            set
            {
                parent = value;
            }
        }

        bool enableConstraints = true;
        public bool EnableConstraintsFixing
        {
            get
            {
                return enableConstraints;
            }
            set
            {
                enableConstraints = value;
            }
        }

        private bool enableAllConstraints = true;
        public bool EnableAllConstraintsFixing
        {
            get
            {
                return enableAllConstraints;
            }
            set
            {
                enableAllConstraints = value;
            }
        }

        private List<Type> criticalContraintTypes;
        public List<Type> CriticalConstraintTypes
        {
            get { throw new NotImplementedException(); }
        }

        private ConnectionInfo acceptedComponentTypesToConnect;
        public ConnectionInfo AcceptedComponentTypesToConnect
        {
            get { return acceptedComponentTypesToConnect; }
        }

        private string article;
        public string Article
        {
            get
            {
                return article;
            }
            set
            {
                article = value;
            }
        }

        private string name;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public IComponent ConnectToEnded(IEndedComponent endedComponent, EndType endToConnect)
        {
            ModelPoint pointToConnect = null;
            if (endToConnect == EndType.Start)
                pointToConnect = (ModelPoint)endedComponent.StartPoint.Clone();
            else if (endToConnect == EndType.End)
                pointToConnect = (ModelPoint)endedComponent.EndPoint.Clone();

            if (pointToConnect == null)
                return null;
            StickConnectionType sticked;
            IComponent spacer = null;
            ModelPoint nearestPoint = this.GetNearestPointOnComponent(pointToConnect, StickConnectionType.ToUpBorder, out sticked, out spacer, ProjectionType.Projection);
            if (spacer == null)
                return null;

            spacer.ConnectToEnded(endedComponent, endToConnect);
            return spacer;
        }

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate BeforeFixingConstraint;

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate AfterFixingConstraint;

        public event FatalModelErrorDelegate FatalModelError;


        public void BeforeFixingInnerComponentConstraintEvent(IComponent sender, ComponentConstraintEventArgs e)
        {
            if (this.BeforeFixingConstraint != null)
                this.BeforeFixingConstraint.Invoke(sender, e);
        }

        public void AfterFixingInnerComponentConstraintEvent(IComponent sender, ComponentConstraintEventArgs e)
        {
            if (this.AfterFixingConstraint != null)
                this.AfterFixingConstraint.Invoke(sender, e);
        }

        public void FatalModelErrorEvent(IComponent sender, FatalErrorEventArgs e)
        {
            if (this.FatalModelError != null)
                this.FatalModelError.Invoke(sender, e);
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, 
            StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            List<ModelPoint> nearestPoints = new List<ModelPoint>();
            foreach (IComponent innerComponent in this.innerComponents)
            {
                ModelPoint resultPoint = innerComponent.GetNearestPointOnComponent(point, stickType, out sticked, projectionType);
                resultPoint.Data.Add(sticked);
                nearestPoints.Add(resultPoint);
            }
            ModelPoint result = ModelCalculations.SortByDistance(point, nearestPoints.ToArray())[0];
            sticked = (StickConnectionType)result.Data[0];
            result.Data.Clear();
            return result;
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, 
            StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            List<ModelPoint> nearestPoints = new List<ModelPoint>();

            foreach (IComponent _innerComponent in this.innerComponents)
            {
                ModelPoint resultPoint = _innerComponent.GetNearestPointOnComponent(point,
                    stickType, out sticked, out innerComponent, projectionType);
                resultPoint.Data.Add(sticked);
                resultPoint.Data.Add(_innerComponent);
                nearestPoints.Add(resultPoint);
            }
            ModelPoint result = ModelCalculations.SortByDistance(point, nearestPoints.ToArray())[0];
            sticked = (StickConnectionType)result.Data[0];
            innerComponent = result.Data[1] as IComponent;
            result.Data.Clear();
            return result;
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, 
            ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            List<ModelPoint> nearestPoints = new List<ModelPoint>();
            foreach (IComponent innerComponent in this.innerComponents)
            {
                ModelPoint resultPoint = innerComponent.GetNearestPointOnComponent(point,
                    measurePoint, stickType, out sticked, projectionType);
                resultPoint.Data.Add(sticked);
                nearestPoints.Add(resultPoint);
            }
            ModelPoint result = ModelCalculations.SortByDistance(point, nearestPoints.ToArray())[0];
            sticked = (StickConnectionType)result.Data[0];
            result.Data.Clear();
            return result;
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, 
            ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            List<ModelPoint> nearestPoints = new List<ModelPoint>();
            foreach (IComponent _innerComponent in this.innerComponents)
            {
                ModelPoint resultPoint = _innerComponent.GetNearestPointOnComponent(point,
                    measurePoint, stickType, out sticked, out innerComponent, projectionType);
                resultPoint.Data.Add(sticked);
                resultPoint.Data.Add(_innerComponent);
                nearestPoints.Add(resultPoint);
            }

            ModelPoint result = ModelCalculations.SortByDistance(point, nearestPoints.ToArray())[0];
            sticked = (StickConnectionType)result.Data[0];
            innerComponent = result.Data[1] as IComponent;
            result.Data.Clear();
            return result;
        }

        private bool selected;
        public bool Selected
        {
            get
            {
                return selected;
            }
            set
            {
                selected = value;
            }
        }

        public bool SelectByPoint(ModelPoint point)
        {
            throw new NotImplementedException();
        }

        public bool IsPointInComponent(ModelPoint point)
        {
            throw new NotImplementedException();
        }

        private List<IComponent> componentsInTolerance;

        public List<IComponent> ComponentsInTolerance
        {
            get { return componentsInTolerance; }
        }

        public bool AutoConnectComponentsInTolerance
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void DisconnectComponentsInTolerance()
        {
            throw new NotImplementedException();
        }

        public void DisconnectFromAll()
        {
            throw new NotImplementedException();
        }

        public void MoveToZeroPoint()
        {
            throw new NotImplementedException();
        }

        public void MoveToZeroXPoint()
        {
            throw new NotImplementedException();
        }

        public void MoveToZeroYPoint()
        {
            throw new NotImplementedException();
        }

        public bool InaccessibleFromMaterials
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public int UniqueId
        {
            get {
     
                throw new NotImplementedException(); 
            }
        }

        public List<int> AcceptedUniqueIdsToConnect
        {
            get { throw new NotImplementedException(); }
        }

        public bool UniqueIdsEnabled
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public IComponent Copy()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members

        public void Visit(Model model)
        {
        }

        public void Visit(Connector connector)
        {
        }

        public void Visit(Shpross shpross)
        {
        }

        public void Visit(Spacer spacer)
        {
        }

        public void Visit(ShprossConnectorStub stub)
        {
        }

        public void Visit(ShprossConnector shprossConnector)
        {
        }

        public void Visit(SpacerElement spacerElement)
        {
        }

        public void Visit(SpacerConnector spacerConnector)
        {
        }

        public void Visit(ModelCell modelCell)
        {
        }

        public void Visit(ModelContainer modelContainer)
        {
        }

        public void Visit(StickConnector stickConnector)
        {
        }

        public void Visit(SnapOnCap snapOnCap)
        {
        }

        public void Visit(ShprossEndToEndConnector endToEndConnector)
        {
        }

        public void Visit(ShprossChain shprossChain)
        {
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SpacerContainer spacerContainer)
        {
         //   throw new NotImplementedException();
        }

        #endregion

        #region IEndlessComponent Members

        public double Size
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public Angle Angle
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(CrossConnector crossConnector)
        {
      //      throw new NotImplementedException();
        }

        #endregion
    }
}
